// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLExprImpl;
// import com.alibaba.druid.sql.ast.SQLObject;
// import com.alibaba.druid.sql.ast.SQLReplaceable;
// import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
// import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr;
// import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.util.Collections;
// import java.util.List;

#include "OracleIntervalExpr.h"
#include "../../../../ast/SQLExpr.h"
#include "../../../../ast/SQLObject.h"
#include "../../../../ast/expr/SQLIntegerExpr.h"
#include "../../visitor/OracleASTVisitor.h"
#include "../../../../visitor/SQLASTVisitor.h"
#include "OracleIntervalType.h"

OracleIntervalExpr::OracleIntervalExpr()
{
}

// @Override
SQLObject_ptr OracleIntervalExpr::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<OracleIntervalExpr>(new OracleIntervalExpr());
  std::shared_ptr<OracleIntervalExpr> x = std::dynamic_pointer_cast<OracleIntervalExpr>(clone_tmp);
  // OracleIntervalExpr x = new OracleIntervalExpr();
  if (value != nullptr)
  {
    x->setValue(std::dynamic_pointer_cast<SQLExpr>(value->clone()));
  }
  x->type = type;
  x->precision = precision;
  x->factionalSecondsPrecision = factionalSecondsPrecision;
  x->toType = toType;
  x->toFactionalSecondsPrecision = toFactionalSecondsPrecision;
  return x;
}

// @Override
BOOL_ptr OracleIntervalExpr::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (this->value == expr)
  {
    setValue(target);
    return BOOL::TRUE;
  }

  if (this->precision == expr)
  {
    setPrecision(target);
    return BOOL::TRUE;
  }

  if (this->toFactionalSecondsPrecision == expr)
  {
    setToFactionalSecondsPrecision(target);
    return BOOL::TRUE;
  }

  return BOOL::FALSE;
}

// @Override
SQLObject_list_ptr OracleIntervalExpr::getChildren()
{
  SQLObject_list_ptr tmp = std::make_shared<std::list<SQLObject_ptr>>();
  tmp->push_back(this->value);
  return tmp;
}

SQLExpr_ptr OracleIntervalExpr::getValue()
{
  return this->value;
}

void OracleIntervalExpr::setValue(SQLExpr_ptr value)
{
  if (value != nullptr)
  {
    value->setParent(SharedObject(OracleIntervalExpr));
  }
  this->value = value;
}

OracleIntervalType_ptr OracleIntervalExpr::getType()
{
  return this->type;
}

void OracleIntervalExpr::setType(OracleIntervalType_ptr type)
{
  this->type = type;
}

SQLExpr_ptr OracleIntervalExpr::getPrecision()
{
  return this->precision;
}

void OracleIntervalExpr::setPrecision(int precision)
{
  this->setPrecision(SQLIntegerExpr_ptr(new SQLIntegerExpr(precision)));
}

void OracleIntervalExpr::setPrecision(SQLExpr_ptr precision)
{
  if (precision != nullptr)
  {
    precision->setParent(SharedObject(OracleIntervalExpr));
  }
  this->precision = precision;
}

int OracleIntervalExpr::getFactionalSecondsPrecision()
{
  return this->factionalSecondsPrecision;
}

void OracleIntervalExpr::setFactionalSecondsPrecision(int factionalSecondsPrecision)
{
  this->factionalSecondsPrecision = factionalSecondsPrecision;
}

OracleIntervalType_ptr OracleIntervalExpr::getToType()
{
  return this->toType;
}

void OracleIntervalExpr::setToType(OracleIntervalType_ptr toType)
{
  this->toType = toType;
}

SQLExpr_ptr OracleIntervalExpr::getToFactionalSecondsPrecision()
{
  return this->toFactionalSecondsPrecision;
}

void OracleIntervalExpr::setToFactionalSecondsPrecision(SQLExpr_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(OracleIntervalExpr));
  }
  this->toFactionalSecondsPrecision = x;
}

// @Override
void OracleIntervalExpr::accept0(SQLASTVisitor_ptr visitor)
{
  this->accept0(std::dynamic_pointer_cast<OracleASTVisitor>(visitor));
}

// @Override
void OracleIntervalExpr::accept0(OracleASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(OracleIntervalExpr)))
  {
    this->acceptChild(visitor, value);
    this->acceptChild(visitor, precision);
    this->acceptChild(visitor, toFactionalSecondsPrecision);
  }
  visitor->endVisit(SharedObject(OracleIntervalExpr));
}

// @Override
int OracleIntervalExpr::hashCode()
{
  int prime = 31;
  int result = 1;
  // result = prime * result + ((factionalSecondsPrecision == nullptr) ? 0 : factionalSecondsPrecision.hashCode());
  result = prime * result + factionalSecondsPrecision;
  result = prime * result + ((precision == nullptr) ? 0 : precision->hashCode());
  result = prime * result + ((toFactionalSecondsPrecision == nullptr) ? 0 : toFactionalSecondsPrecision->hashCode());
  result = prime * result + ((toType == nullptr) ? 0 : typeid(*toType).hash_code()); // toType->hashCode());
  result = prime * result + ((type == nullptr) ? 0 : typeid(*type).hash_code());     // type->hashCode());
  result = prime * result + ((value == nullptr) ? 0 : value->hashCode());
  return result;
}

// @Override
bool OracleIntervalExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == nullptr)
  {
    return false;
  }
  if (getClass()->c_str() != obj->getClass()->c_str())
  {
    return false;
  }
  OracleIntervalExpr_ptr other = std::dynamic_pointer_cast<OracleIntervalExpr>(obj);
  // if (factionalSecondsPrecision == nullptr) {
  //     if (other->factionalSecondsPrecision != nullptr) {
  //         return BOOL::FALSE;
  //     }
  // } else if (!factionalSecondsPrecision.equals(other->factionalSecondsPrecision)) {
  //     return BOOL::FALSE;
  // }
  if (factionalSecondsPrecision != other->factionalSecondsPrecision)
  {
    return false;
  }
  if (precision == nullptr)
  {
    if (other->precision != nullptr)
    {
      return false;
    }
  }
  else if (!precision->equals(other->precision))
  {
    return false;
  }
  if (toFactionalSecondsPrecision == nullptr)
  {
    if (other->toFactionalSecondsPrecision != nullptr)
    {
      return false;
    }
  }
  else if (!toFactionalSecondsPrecision->equals(other->toFactionalSecondsPrecision))
  {
    return false;
  }
  if (toType != other->toType)
  {
    return false;
  }
  if (type != other->type)
  {
    return false;
  }
  if (value == nullptr)
  {
    if (other->value != nullptr)
    {
      return false;
    }
  }
  else if (!value->equals(other->value))
  {
    return false;
  }
  return true;
}
